package Core;

import java.io.File;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;

import VBox.PortForward;

import VBox.VMTask;

public class Dispatcher implements Runnable{
	Logger log = Logger.getAnonymousLogger();
	DB db = new DB();
	private String VDI_SOURCE_DIR;
	private String VDI_TARGET_DIR;
	static String CREATEVMSCRIPT;
	String MYIP;
	String ZEUSPORT,ZEUSIP;
	private String STARTVMSCRIPT;
	private String MODIFYVMSCRIPT;
	private String STOPVMSCRIPT;
	private String DELETEVMSCRIPT;
	
	public static void main(String args[]) {
		Settings settings = new Settings();
		Dispatcher disp = new Dispatcher(settings.VMMSettings);
		disp.run();
	}
	public Dispatcher(Properties props) {
		
		
		VDI_SOURCE_DIR = props.getProperty("vmm.vdisourcedir","/vdi");
		VDI_TARGET_DIR = props.getProperty("vmm.vditargetdir","/clients");
		CREATEVMSCRIPT = props.getProperty("vmm.scriptsdir","./scripts")+"createvm.sh";
		String scriptPath = props.getProperty("vmm.scriptsdir","./scripts");
		STARTVMSCRIPT = scriptPath +"startvm_new.sh";
		STOPVMSCRIPT = scriptPath +"stopvm.sh";
		MODIFYVMSCRIPT = scriptPath +"modifyvm.sh";
		DELETEVMSCRIPT = scriptPath +"deletevm.sh";
		ZEUSIP = props.getProperty("vmm.zeusip","5000");
		ZEUSPORT = props.getProperty("vmm.zeusclientport","5000");
		
	}
	
	public void run() {
		int waitTime = 5;    // in seconds
		int jobCount;
		while(true) {
			jobCount = getJobCount();
			if(jobCount == 0)
				{
				log.info("No jobs");
				waitTime *=1.5;
				log.info("Wait TIME : "+waitTime);
				try {
					Thread.sleep(1000*waitTime);
				} catch (InterruptedException e) {
					
					log.severe("ERROR in tnread.sleep()");
				}
				}
			else
				{
				waitTime = 10;
				executeJob();
				}
		}
	}

	private void executeJob() {
		try {
			Statement st;
			st = db.getCon().createStatement();
			ResultSet res = st.executeQuery("SELECT id,job FROM  jobs where status='pending' limit 1");
        
       while (res.next()) {
          int  jobID = res.getInt("id");
          String job = res.getString("job");
          log.info("jobID : "+jobID);
          String params[] = job.split(" ");
          updateJob(jobID,"running");
          if(params[0].equalsIgnoreCase("START")) {
        	  int ret = start(params,jobID+"");
        	  if(ret == 0) updateJob(jobID, "completed");
        	else updateJob(jobID, "error");
          }
          
          else if(params[0].equalsIgnoreCase("STOP")) {
        	  int ret = stop(params);
        	  if(ret == 0) updateJob(jobID, "completed");
          	else updateJob(jobID, "error");
            }
          
          else if(params[0].equalsIgnoreCase("MODIFY")) {
        	  int ret = modify(params);
        	  if(ret == 0) updateJob(jobID, "completed");
          	else updateJob(jobID, "error");
            }
          
          else if(params[0].equalsIgnoreCase("DELETE")) {
        	  int ret = delete(params);
        	  if(ret == 0) updateJob(jobID, "completed");
          	else updateJob(jobID, "error");
            }
          
          else if (params[0].equalsIgnoreCase("CREATE")) {
        	  int ret = create(params);
        	  if(ret == 0) updateJob(jobID, "completed");
          	else updateJob(jobID, "error");
            }
	}
       return;
	}
	catch (SQLException e) {
		
		log.info(e.getMessage());
	}
		
	}

	private int modify(String[] params) {
		String name = params[1];
		String ram = params[2];
		String hdd = params[3];
		String cores = params[4];
		
		
		if(isVM(name) == false) {
			log.info("Invalid VM Name.. Aborting..");
			return -1;
	}
		String cmd=MODIFYVMSCRIPT+" "+name+" "+ram+" "+  hdd + " " + cores+" "+VDI_SOURCE_DIR;
		
		log.info("modifyVM : Going to execute "+cmd);
		
		VMTask task = new VMTask();
		task.cmd = cmd;
		int ret = task.call();
		
		if(true)
			{
			log.info("Succesfully modified VM "+name);
			try {
				Statement st;
				st = db.getCon().createStatement();
				String query = "UPDATE vms SET ram="+ram+", cores="+cores+", hdd=hdd+"+hdd+" where vmname='"+name+"'" ;
				log.info("Executing : "+query);
				
				st.executeUpdate(query);
	        
	       log.info("Successfully updated entry in DB");
	       return 0;
		}
		catch (SQLException e) {
			log.severe("Error updating DB");
			return -1;
		}
			
		}
		
		else
			log.severe("Error modifying VM "+name);
		return ret;
	}

	private int delete(String[] params) {
		String name = params[1];
		
		if(isVM(name) == false) {
			log.info("Invalid VM Name.. Aborting..");
			return -1;
	}
		
		log.info("Deleting VM "+name);
		VMTask task = new VMTask();
		String cmd = DELETEVMSCRIPT+ " "+ name;
		task.cmd = cmd;
		//job.add("DELETE",vm);
		task.call();
		deleteFromDB(name);
		//Thread newVMThread = new Thread(task);
		//newVMThread.start();
		log.info("\t Task submitted to thread");
		return 0;
	}

	
	private int create(String[] params) {
		String name = params[1];
		String OS = params[2];
		String ram = params[3];
		String hdd = params[4];
		String cores = params[5];
		
		int vrdpPort = 3000;
		
		if(isVM(name)) {
				log.info("A VM already exists with this name.. Aborting..");
				return -1;
				
		}
		String osVdi = VDI_SOURCE_DIR+File.separator+OS+".vdi";
		String cmd=CREATEVMSCRIPT+" "+name+" "+OS+" "+ram+" "+vrdpPort+" "+ MYIP +" "+ osVdi + " " + VDI_TARGET_DIR + " " +  hdd + " " + cores + " " + ZEUSPORT;
		
		log.info("CreateVM : Going to execute "+cmd);
		
		VMTask task = new VMTask();
		task.cmd = cmd;
		
		int ret = task.call();
		//int ret = job.add("CREATE", vm);
		
		if(ret == 0)
			{
			log.info("Succesfully created VM "+name);
			insertInDB(params);
			}
		else
			log.severe("Error creating VM "+name);
		return ret;
	}

	private int stop(String[] params) {
		String name = params[1];
		
		if(isVM(name) == false) {
			log.info("Invalid VM Name.. Aborting..");
			return -1;
	}
		log.info("Stoping VM "+name);
		VMTask task = new VMTask();
		String cmd = STOPVMSCRIPT+ " "+ name;
		task.cmd = cmd;
		PortForward pf = new PortForward();
		pf.unreserve(name);
		task.call();
		//job.add("STOP", vm);
		//Thread newVMThread = new Thread(task);
		//newVMThread.start();
		log.info("\t Task submitted to thread");
		return 0;
	}

	private void updateJob(int jobID, String status) {
		try {
			Statement st;
			st = db.getCon().createStatement();
			String query = "UPDATE jobs set status='"+status+"' where id="+jobID; 
			log.info("Executing : "+query);
			
			st.executeUpdate(query);
			
       log.info("Successfully added entry in DB");
       return;
	}
	catch (SQLException e) {
		log.severe("Error updating DB");
		log.severe(e.getMessage());
		
	}
	return;
	}

	private int start(String[] params,String jobID) {
		String name = params[1];
		if(isVM(name) == false) {
			log.info("Invalid VM Name.. Aborting..");
			return -1;
	}
		
		
		log.info("Starting VM "+name);
		VMTask task = new VMTask();
		//String cmd = STARTVMHEADLESS.replaceFirst("VMNAME",name);
		//String cmd = Hyperviser.Commands.STARTVMHEADLESS.replaceFirst("VMNAME",name);
		PortForward pf = new PortForward();
	//	String cmd = STARTVMSCRIPT+ " "+ name+" "+pf.getPort(vm)+ " 22 &";
		String cmd = STARTVMSCRIPT+ " "+ name+" "+pf.getPort(name,jobID)+ " "+ZEUSPORT+" "+ZEUSIP+" &";
		task.cmd = cmd;
		//Thread newVMThread = new Thread(task);
		//newVMThread.start();
		
		ExecutorService pool = Executors.newFixedThreadPool(1);
		pool.submit(task);
		//job.add("START", vm);
		log.info("\t Task submitted to thread");
		return 0;
	}

	private int getJobCount() {
		try {
			Statement st;
			st = db.getCon().createStatement();
			ResultSet res = st.executeQuery("SELECT count(*) as count FROM  jobs where status='pending'");
        
       while (res.next()) {
          int  count = res.getInt("count");
          return count;
	}
       return 0;
	}
	catch (SQLException e) {
		
		log.info(e.getMessage());
	}
		
		return 0;
	}
	
	
	boolean isVM(String vmname) {
		try {
			Statement st;
			st = db.getCon().createStatement();
			ResultSet res = st.executeQuery("SELECT status FROM  vms where vmname='"+vmname+"'");
        
       while (res.next()) {
          String status = res.getString("status");
          if(status.equalsIgnoreCase("active"))
        		  { log.info("valid Vm"); return true; }
          else return false;
	}
       return false;
	}
	catch (SQLException e) {
		
		log.info(e.getMessage());
	}
		return true;
	}
	
	
	
	/**
	 * Inserts an entry in the DB when creating a new VM
	 * @param vm A VM object
	 * @return Errorcode
	 */
	int insertInDB(String params[]) {
		String name = params[1];
		String OS = params[2];
		String ram = params[3];
		String hdd = params[4];
		String cores = params[5];
		
		
		try {
			Statement st;
			st = db.getCon().createStatement();
			String query = "INSERT INTO vms(vmname,owner,ram,hdd, cores,os) values ('"+name+"' , 'guest1', "+ ram+" , "+hdd+", "+cores+", '"+OS+"') " ; 
			log.info("Executing : "+query);
			
			st.executeUpdate(query);
			
       log.info("Successfully added entry in DB");
       return 0;
	}
	catch (SQLException e) {
		log.severe("Error updating DB");
		log.severe(e.getMessage());
		
		return -1;
	}
		
	}
	
	/**
	 * Sets status of a VM to 'deleted' in DB
	 * @param vm
	 * @return
	 */
	int deleteFromDB(String  vm) {
		try {
			Statement st;
			st = db.getCon().createStatement();
			String query = "UPDATE vms SET status='deleted' where vmname='"+vm+"'" ;
			log.info("Executing : "+query);
			
			st.executeUpdate(query);
        
       log.info("Successfully updated entry in DB");
       return 0;
	}
	catch (SQLException e) {
		log.severe("Error updating DB");
		return -1;
	}
		
	}
}
